package edu.ua.bmr.android;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.AlertDialog.Builder;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Display;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo;
import edu.ua.bmr.android.BMR.Users;

public class LoginActivity extends ListActivity implements Runnable,
		LoginDialogListener {

	public static final String TAG = "LoginActivity";
	public static final String[] FROM_COLUMNS = { Users.USERNAME };
	public static final int[] TO_VIEWS = { R.id.loginitem_text };

	private LoginDialog mLoginDialog;
	private Dialog mWaitDialog;
	private SimpleCursorAdapter mCursorAdapter;
	private TextView mFooter;
	private String mUsername;
	private String mPassword;
	private boolean mRememberPassword = false;
	private SQLiteDatabase mDatabase;
	private DatabaseHelper mDatabaseHelper;

	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		setContentView(R.layout.act_login);
		
		mDatabaseHelper = new DatabaseHelper(this);
		loadDatabase();

		mFooter = new TextView(this);
		mFooter.setTextSize(25);
		mFooter.setPadding(20, 20, 20, 20);
		mFooter.setTextColor(Color.GRAY);
		mFooter.setText(R.string.enter_new_user);
		mFooter.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
		
		getListView().setCacheColorHint(Color.TRANSPARENT);
		getListView().addFooterView(mFooter);
		registerForContextMenu(getListView());
		updateListview();
		

		setTitle(R.string.app_title);
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		mDatabase.close();
		mDatabaseHelper.close();
	}

//	@Override
//	public void onContentChanged() {
//		super.onContentChanged();
//	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.menu_login, menu);		
	    return true;
	}
	
	/* Handles item selections */
	public boolean onOptionsItemSelected(MenuItem item) {
	    switch (item.getItemId()) {
	    case R.id.menu_new_user:
			showDialog(0);
	        return true;
	    }
	    return false;
	}
	
	@Override
	public void onCreateContextMenu(ContextMenu menu, View v,
			ContextMenuInfo menuInfo) {

		super.onCreateContextMenu(menu, v, menuInfo);
		AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
		ListView list = LoginActivity.this.getListView();

		if (info != null) {

			if (info.position < list.getCount() - 1) { // not the header

				// Get the selected user name
				Cursor cursor = (Cursor) getListView().getItemAtPosition(
						info.position);
				int usrindex = cursor.getColumnIndex(Users.USERNAME);
				int pwindex = cursor.getColumnIndex(Users.PASSWORD);
				String username = cursor.getString(usrindex);
				String password = cursor.getString(pwindex);

				// Init the menu
				Resources r = getResources();
				String title = r.getString(R.string.login_contextmenu_title);
				menu.setHeaderTitle(title + " " + username);
				MenuInflater inflater = getMenuInflater();
				inflater.inflate(R.menu.contextmenu_login, menu);
				
				// Grab a session
				Session s = Session.getInstance();
				
				// Grab the variable MenuItems
				MenuItem logout = menu.findItem(R.id.menu_logout);
				MenuItem forget = menu.findItem(R.id.menu_forget_password);
				
				// If there is a password, allow the user the option to forget it
				forget.setVisible( password.length() > 0 ); 
				
				// If the user is logged in, let them log out
				logout.setVisible( s.isLoggedIn(username) );
			}
		}

	}

	@Override
	public boolean onContextItemSelected(MenuItem item) {

		// Get the selected user name
		AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
				.getMenuInfo();
		Cursor cursor = (Cursor) getListView().getItemAtPosition(info.position);
		int userIndex = cursor.getColumnIndex(Users.USERNAME);
		String username = cursor.getString(userIndex);
		Resources r = getResources();
		
		int id = (int) item.getItemId();

		// Which menu item did we select?
		switch (id) {

		case R.id.menu_delete_user: // Delete user
			deleteUser(info.id);
			Toast.makeText(LoginActivity.this, r.getString( R.string.deleted_user ) + " " + username,
					Toast.LENGTH_SHORT).show();
			updateListview();
			break;
			
		case R.id.menu_forget_password:
			updatePassword(username, "");
			Toast.makeText(LoginActivity.this, R.string.password_forgotten,
					Toast.LENGTH_SHORT).show();
			updateListview();
			break;
			
		case R.id.menu_logout:
			Session.getInstance().logout();
			break;
			
		default: break;
		}
		return true;
	}

	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {

		mUsername = null;
		mPassword = null;

		if (position == l.getAdapter().getCount() - 1) {
			showDialog(0); // New user
		}		
		else { // Existing user

			// Grab the username the user selected.
			SQLiteCursor cursor = (SQLiteCursor) l.getItemAtPosition(position);

			int username = cursor.getColumnIndex(Users.USERNAME);
			int password = cursor.getColumnIndex(Users.PASSWORD);

			mUsername = cursor.getString(username);
			mPassword = cursor.getString(password);
			
			// Let's make sure the requested user isn't already logged in
			Session s = Session.getInstance();
			if(s.isLoggedIn(mUsername)) {
				startSemesterActivity();
				return;
			}
			else if(s.isLoggedIn()) {
				// This action will log the current user off.
				
				String msg = String.format(getString(R.string.confirm_logout_msg), s.getUsername());
				DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						switch(which) {
						case DialogInterface.BUTTON_POSITIVE:
							// mUsername and mPassword have been set. Just do the login.
							doLogin();
							break;
						}
						dialog.dismiss();
					}
				};
				
				Builder builder = new AlertDialog.Builder(this);
				builder.setTitle(R.string.confirm_logout_title)
					.setMessage(msg)
					.setPositiveButton(R.string.yes, listener)
					.setNegativeButton(R.string.no, listener)
					.show();
				return;
			}

			if (mPassword.length() == 0) {
				showDialog(0);
			} else {
				doLogin();
			}
		}
	}

	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {
		super.onPrepareDialog(id, dialog);
		((LoginDialog) dialog).setUser(mUsername);
	}

	@Override
	protected Dialog onCreateDialog(int id) {

		mLoginDialog = new LoginDialog(this, this);
		mLoginDialog.setContentView(R.layout.dialog_login);

		return mLoginDialog;
	}

	public boolean onCancelClick() {
		mUsername = null;
		mPassword = null;
		return true; // Close the dialog
	}

	public boolean onOkClick(String username, String password, boolean rememberPassword) {

		mLoginDialog.hideSoftKeyboard();
		
		mUsername = username;
		mPassword = password;
		mRememberPassword = rememberPassword;

		// Start a new thread to perform login
		doLogin();
		return false; // Keep the dialog open for now
	}

	private void doLogin() {
		// Tell the user to wait a bit...
		mWaitDialog = BMR.showProgressDialog(this, R.string.please_wait, R.string.logging_in);

		// Spawn a thread to handle the logging in request.
		Thread t = new Thread(LoginActivity.this);
		t.start();
	}

	/**
	 * This thread just attempts to log in using mUsername and mPassword.
	 */
	public void run() {
		// Get our instance of Session...
		Session s = Session.getInstance();

		// Try to login. Don't worry about displaying the exception.
		try {
			// Success case
			s.login(mUsername, mPassword);
			Message msg = new Message();
			msg.arg1 = BMR.MESSAGE_LEGAL;
			mHandler.sendMessage(msg);
		} catch (BMRException e) {
			// Failure case
			Message msg = new Message();
			msg.arg1 = BMR.MESSAGE_EXCEPTION;
			msg.arg2 = e.getResourceId();
			mHandler.sendMessage(msg);
		}
	}
	
	/**
	 * Handles messages passed by our login thread.
	 */
	private Handler mHandler = new Handler() {
		public void handleMessage(Message msg) {
			mWaitDialog.dismiss();

			// Make sure there were no problems
			if (msg.arg1 == BMR.MESSAGE_LEGAL) {
				// First make sure the Session is actually in a logged in state.
				if (Session.getInstance().isLoggedIn()) {
					
					// Close the login dialog
					if ( mLoginDialog != null && mLoginDialog.isShowing() ) {
						mLoginDialog.dismiss();
					}
					
					
					// Add the user if he's new
					if ( !userExists(mUsername) ) {
						addUser(mUsername);
					}
					
					// Remember the password if selected
					if ( mRememberPassword ) {
						updatePassword(mUsername, mPassword);
					}
					
					// Always update the ListView so we can update the state of the "logged in" icon
					updateListview();
					
					startSemesterActivity();
				}
				else {
					Toast.makeText(LoginActivity.this, "Login failed", Toast.LENGTH_SHORT).show();
				}

			} else if(msg.arg1 == BMR.MESSAGE_EXCEPTION) {
				// We had some other miscellaneous error. Let the user know.
				// msg.arg2 contains a user-friendly string resource ID.
				Builder builder = new AlertDialog.Builder(LoginActivity.this);
				builder.setTitle(R.string.login_failed);
				builder.setMessage(msg.arg2);
				builder.setPositiveButton(R.string.button_ok, null);
				builder.show();
			}
			else {
				throw new IllegalArgumentException("Login handler passed an illegal message code.");
			}

			// We've consume our data and don't need it any longer.
			mUsername = null;
			mPassword = null;
			mRememberPassword = false;
		}


	};
	
	/**
	 * Starts SemesterActivity only if the Session is in a logged-in state.
	 */
	private void startSemesterActivity() {
		Session s = Session.getInstance();
		if(s.isLoggedIn()) {
			// Start the next Activity
			Context c = (Context) LoginActivity.this;
			Intent i = new Intent(c, SemesterActivity.class);
			startActivity(i);
		}	
	}
	/**
	 * The following code is all relating to the database which the system
	 * stores usernames and passwords.
	 * 
	 * @author pkilgo
	 * 
	 */

	private static class DatabaseHelper extends SQLiteOpenHelper {

		DatabaseHelper(Context context) {
			super(context, BMR.DB_NAME, null, BMR.DB_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			String sql = "CREATE TABLE IF NOT EXISTS " + BMR.DB_TABLE_USERS
					+ " (" + Users._ID + " INTEGER PRIMARY KEY autoincrement, "
					+ Users.USERNAME + " VARCHAR, " + Users.PASSWORD
					+ "  VARCHAR);";

			db.execSQL(sql);
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
					+ newVersion + ", which will destroy all old data");
			db.execSQL("DROP TABLE IF EXISTS " + BMR.DB_TABLE_USERS);
			onCreate(db);
		}

	}

	/**
	 * Load the database and create it if necessary. Also, set up the tables.
	 */
	private void loadDatabase() {
		// Load and create the database if necessary
		mDatabase = mDatabaseHelper.getWritableDatabase();
	}

	/**
	 * Once the database has been changed, we call this method to update the UI
	 * component to reflect the new changes.
	 */
	private void updateListview() {
		// Requery if the cursor has already been created.
		if (mCursorAdapter != null) {
			Cursor c = mCursorAdapter.getCursor();
			c.requery();
			return;
		}

		// Create the new Cursor since there ain't one.
		Cursor c = this.queryAllUsernames();
		mCursorAdapter = new SimpleCursorAdapter(this, R.layout.loginitem_row,
				c, FROM_COLUMNS, TO_VIEWS);
		setListAdapter(mCursorAdapter);
	}

	/**
	 * Insert a username with no password into the database.
	 * 
	 * @param username
	 *            , username to add
	 * @return true on success. false on failure.
	 */
	private boolean addUser(String username) {
		return addUser(username, "");
	}

	/**
	 * Insert a username and password into the database.
	 * 
	 * @param username
	 *            , username to add
	 * @param password
	 *            , password to add
	 * @return true on success, false on failure
	 */

	private boolean addUser(String username, String password) {

		// Lowercase only, please;

		// If we got null, just set it to blank.
		if (password == null)
			password = "";

		// Let's not add the user if they already exist.
		if (userExists(username))
			return false;

		ContentValues values = new ContentValues();

		values.putNull(Users._ID);
		values.put(Users.USERNAME, username);
		values.put(Users.PASSWORD, password);

		long result = mDatabase.insert(BMR.DB_TABLE_USERS, null, values);

		return result >= 0;
	}

	private boolean userExists(String username) {
		Cursor c = queryUser(username);
		if (c.getCount() > 0) {
			c.close();
			return true;
		} else {
			c.close();
			return false;
		}
	}
	
	/**
	 * Make a query for a specific username.
	 * 
	 * @param username
	 * @return Cursor over the result.
	 */
	private Cursor queryUser(String username) {
		username = username.toLowerCase();
		return mDatabase.query(BMR.DB_TABLE_USERS, new String[] { Users._ID,
				Users.USERNAME, Users.PASSWORD }, Users.USERNAME + "='" + username + "'", null,
				null, null, null, "1");
	}

	/**
	 * Query for all the rows in the database.
	 * 
	 * @return Cursor over the result
	 */
	private Cursor queryAllUsernames() {
		return mDatabase.rawQuery("SELECT " + Users._ID + ", " + Users.USERNAME
				+ ", " + Users.PASSWORD + " FROM " + BMR.DB_TABLE_USERS, null);
	}

	private void updatePassword(String username, String password) {
		if (userExists(username) == false)
			return;

		ContentValues values = new ContentValues();
		values.put(Users.PASSWORD, password);
		mDatabase.update(BMR.DB_TABLE_USERS, values, Users.USERNAME + " = '"
				+ username + "'", null);
	}

	private int deleteUser(long id) {
		return mDatabase.delete(BMR.DB_TABLE_USERS, Users._ID + " = " + id,
				null);
	}

	// Ben: We can delete these functions at some point, but they help me out
	// for debugging

	private void deleteAllUsers() {
		mDatabase.delete(BMR.DB_TABLE_USERS, null, null);
	}

	public static void log(String message) {
		Log.v(TAG, message);
	}

	public static void logException(Exception ex) {
		log(ex.getMessage());
	}

	private void toast(String message) {
		Toast.makeText(LoginActivity.this, message, Toast.LENGTH_LONG).show();
	}

	private void toastException(Exception ex) {
		toast("Exception: " + ex.getMessage());
	}
}
